Skip to content

Learning Weekly-2025-03-16

The number accuracy problem of JavaScript

toFixed的结果可能会欺骗你【渡一教育】_哔哩哔哩_bilibili

Look at the following code:

ts
1.45.toFixed(1); // 1.4
2.45.toFixed(1); // 2.5

The root cause of the problem is that JavaScript's storage and operation of various numbers are based on binary, and the binary cannot accurately represent the decimal.

Storage precision problem

You can use the .toString(2) method to get the binary representation of a number, and you will find that the binary representation of most decimals is infinitely repeating.

When storing, the decimal part will be truncated. If the truncated position is 0, the result will become smaller, and if the truncated position is 1, it will be rounded up, causing the result to become larger.

You can use the .toPrecision() method to convert the number to a string with the specified precision, and you can clearly see the problem.

For example:

ts
console.log(1.45.toPrecision(50)); // '1.4499999999999999555910790149937383830547332763672'
console.log(2.45.toPrecision(50)); // '2.4500000000000001776356839400250464677810668945313'

Here you can see that after 1.45 is converted, the result is smaller, and after 2.45 is converted, the result is larger.

Operation precision problem

When performing operations, the binary is used for operations, so there may also be precision problems.

For example:

ts
console.log(0.1 + 0.2); // 0.30000000000000004

Display precision problem

Although the storage is not accurate, the display is correct in the console, because the browser does approximate processing.

ts
console.log(0.3) // 0.3
console.log(0.3.toPrecision(50)) // '0.29999999999999998889776975374843459576368331909180'
console.log(0.29999999999999998889776975374843459576368331909180) // 0.3
console.log(0.3 === 0.29999999999999998889776975374843459576368331909180) // true

The precision problem of 1.45 and 2.45 based on the toFixed() method

ts
console.log(1.45.toString(2)) // 1.0111001100110011001100110011001100110011001100110011
console.log(1.45.toPrecision(50)); // '1.4499999999999999555910790149937383830547332763672'
console.log(2.45.toString(2)) // 10.01110011001100110011001100110011001100110011001101
console.log(2.45.toPrecision(50)); // '2.4500000000000001776356839400250464677810668945313'

Because the integer part of 2.45 is 10, which occupies 2 bits, and the integer part of 1.45 is 1, which only occupies 1 bit, so although the decimal part of both is theoretically the same, in actual storage, the storage digits of 2.45 will be less, resulting in different decimal parts after rounding.